<?php
namespace app\api\worker;

use Exception;
use think\cache\driver\Redis;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\Db;
use think\facade\Log;
use think\worker\Server;
use Workerman\Lib\Timer;
use Workerman\Worker;


class ChatWorker extends Server
{
    protected $socket = 'websocket://0.0.0.0:6868';

    protected $option = [
        'name'  =>'chat',
        'count' =>4,
    ];

    protected array $uidConnections = [];  // uid到连接的映射
    protected array $groupConnections = []; // 群组到连接的映射

    public function onWorkerStart($worker)
    {
        $time_interval = 5;
        echo '启动聊天服务'.PHP_EOL;
        Timer::add($time_interval, static function () use ($worker) {
//            echo "当前时间：".date("Y-m-d H:i:s").PHP_EOL;
//            foreach($worker->connections as $connection) {
//                $connection->send(json_encode(['type'=>'ping']));
//            }
        });
        $inner_text_worker = new Worker('text://127.0.0.1:5678');
        //这里的$connection是给推送者调用的
        $inner_text_worker->onMessage = static function ($connection, $buffer) {
            $connection->send( '你好欢迎你...');
        };
        try {
            $inner_text_worker->listen();
        } catch (\Exception $e) {
            echo "启动异常".date("Y-m-d H:i:s").$e->getMessage().PHP_EOL;
            Log::write('chat1启动异常：'.$e->getMessage());
        }
    }

    public function onConnect($connection)
    {
//        $connection->send(json_encode(['message'=>'连接成功','type'=>'conn','uid'=>1]));
        $conn_id = $connection->uid ?? $connection->id;
        echo "有新用户来了：".$conn_id.'时间：'.date("Y-m-d H:i:s").PHP_EOL;
    }

    public function onMessage($connection, $data)
    {
        if (empty($data)){
            echo '没有内容'.PHP_EOL;
        }
        if (is_array($data)){
            $data = json_encode($data, true);
        }else{
            $data = json_decode($data, true);
        }
        $type = $data['type']??'';
        switch($type) {
            case Constant::MESSAGE_TYPE_LOGIN:
                //登录做绑定操作
                $this->bindUid($connection, $data['uid']??0);
                break;
            case Constant::MESSAGE_TYPE_JOIN_GROUP:
                //加入组
                $this->joinGroup($connection, $data['to_uid']??0);
                break;
            case Constant::MESSAGE_TYPE_SEND_TO_UID:
                $flag = $this->checkMessage($data['uid']??0,$type,$data['to_uid']??0);
                if ($flag === false){
                    $connection->send(json_encode(['message'=>'发送频繁','error_msg'=>-1,'type'=>'error_msg']));
                }
                //给某个人发
                $this->sendToUid($data['to_uid']??0, $data);
                break;
            case Constant::MESSAGE_TYPE_SEND_TO_GROUP:
            case Constant::MESSAGE_TYPE_SEND_TO_CUSTOMER:
                $flag = $this->checkMessage($data['uid']??0,$type,$data['to_uid']??0);
                if ($flag === false){
                    $connection->send(json_encode(['message'=>'发送频繁','error_msg'=>-1,'type'=>'error_msg']));
                }
                //给组发
                $this->sendToGroup($data['to_uid']??'', $data);
                break;
//            case Constant::MESSAGE_TYPE_SEND_TO_ALL:
//                //发送所有内容
//                $this->sendToAll($data);
//                break;
            case Constant::MESSAGE_TYPE_PING:
                //给某个人发
                $connection->send(json_encode(['type'=>'ping','message'=>'ok']));
                break;

        }
        return true;
    }

    public function onClose($connection)
    {
        $this->unbindUid($connection);
        $this->leaveAllGroups($connection);
    }

    protected function bindUid($connection, $uid)
    {
        if (empty($uid)){
            echo $connection->id.'没有uid绑定失败'.PHP_EOL;
            $connection->send(json_encode(['message'=>'请先登录','type'=>'error_msg']));
        }
        $this->uidConnections[$uid] = $connection;
        $connection->uid = $uid;
        $offline_message = $this->offline_message($uid);
//        print_r($offline_message);
//        echo PHP_EOL;
        if (!empty($offline_message)){
            foreach ($offline_message as $item) {
                $connection->send(json_encode(['message'=>'请先登录','type'=>'error_msg']));
            }
        }
        echo "用户 {$uid} 绑定成功".date("Y-m-d H:i:s").PHP_EOL;
    }

    protected function unbindUid($connection)
    {
        if(isset($connection->uid)) {
            unset($this->uidConnections[$connection->uid]);
//            $connection->send("User {$connection->uid} disconnected");
            echo "用户 $connection->uid 走了".date("Y-m-d H:i:s").PHP_EOL;
        }
    }

    protected function joinGroup($connection, $group_id)
    {
        if (empty($group_id)){
            echo $connection->id.'没有group_id'.PHP_EOL;
            $connection->send(json_encode(['message'=>'请选择加入的组','type'=>'error_msg']));
        }
        $this->groupConnections[$group_id][$connection->uid] = $connection;
        $connection->groups[] = $group_id;
        echo "用户{$connection->uid}加入组$group_id".PHP_EOL;
    }

    protected function leaveGroup($connection, $group_id)
    {
        if(isset($this->groupConnections[$group_id][$connection->uid])) {
            unset($this->groupConnections[$group_id][$connection->uid]);
            $key = array_search($group_id, $connection->groups);
            if($key !== false) {
                unset($connection->groups[$key]);
            }
        }
    }

    protected function leaveAllGroups($connection)
    {
        if(isset($connection->groups)) {
            foreach($connection->groups as $group_id) {
                $this->leaveGroup($connection, $group_id);
            }
        }
    }

    /***
     * $uid 接受者uid
     * $message 发送内容
     * $my_uid  发送者uid
     */
    protected function sendToUid($uid, $message)
    {
        if (empty($uid)){
            echo '没有接受者uid'.PHP_EOL;
        }
        if (empty($message['uid'])){
            echo '没有发送uid'.PHP_EOL;
        }
        if (empty($message['message'])){
            echo '没有message'.PHP_EOL;
        }
        $message_id = $this->saveMessage($message);
        try {
            if(isset($this->uidConnections[$uid])) {
                //推送给对方
                $this->uidConnections[$uid]->send(json_encode($this->sendData($message,$message_id)));
                if (isset($message['uid']) && isset($message['to_uid'])){
                    if ($message['uid'] != $message['to_uid']){
                        //推送给自己
                        $this->uidConnections[$message['uid']]->send(json_encode($this->sendData($message,$message_id)));
                    }
                }
            }
        }catch (Exception $e){
            Log::write('sendToUid消息推送失败：'.$e->getMessage());
        }

    }

    protected function sendToGroup($group_id, $message)
    {
        if (empty($group_id)){
            echo '没有group_id发送失败:'.PHP_EOL;
        }
        if (empty($message['message'])){
            echo '没有group_message'.PHP_EOL;
        }
        if (empty($message['uid'])){
            echo '没有发送人uid'.PHP_EOL;
        }
        try {
            $message_id = $this->saveMessage($message);
            if(isset($this->groupConnections[$group_id])) {
                foreach($this->groupConnections[$group_id] as $connection) {
                    $connection->send(json_encode($this->sendData($message,$message_id)));
                }
            }
        }catch (Exception $e){
            Log::write('sendToGroup消息推送失败：'.$e->getMessage());
        }

    }

    protected function sendToAll($data){
        if (empty($data['message'])){
            echo '没有发送内容不推送';
        }
        try {
            $this->saveAllMessage($data);
            if(isset($this->uidConnections)) {
                $data['message'] = userTextDecode($data['message']);
                if (is_array($data)){
                    $data = json_encode($data);
                }
                foreach($this->uidConnections as $connection) {
                    $connection->send($data);
                }
            }
        }catch (Exception $e){
            Log::write('sendToAll消息推送失败：'.$e->getMessage());
        }

    }

    //保存内容到MySQL
    public function saveMessage($data){
        try {
            if (!empty($data['uid']) && !empty($data['to_uid']) && !empty($data['message']) && !empty($data['type'])){
                $table = Constant::MESSAGE;
                if ($data['type'] == Constant::MESSAGE_TYPE_SEND_TO_GROUP){
                    $table = Constant::MESSAGE_GROUP;
                }
                if ($data['type'] == Constant::MESSAGE_TYPE_SEND_TO_CUSTOMER){
                    $table = Constant::MESSAGE_CUSTOMER;
                }
                $ins['uid']     = $data['uid'];
                $ins['to_uid']  = $data['to_uid'];
                $ins['message'] = sqlFilter(userTextEncode($data['message']));
                $ins['create_time']    = time();
                return Db::name($table)->insertGetId($ins);
            }
        }catch (Exception $e){
            Log::write('sendToAll消息推送失败：'.$e->getMessage());
        }
        return 0;
    }

    //发送消息验证避免刷消息
    public function checkMessage($uid,$type,$to_uid): bool
    {
        $redis = new Redis();
        $key   = 'checkMessage:'.$uid.':Type:'.$type.':ToUid:'.$to_uid;
        $key_black = 'checkBlackUid:'.$uid;
        $count = $redis->get($key);
        $count = empty($count) ? 1 : $count + 1;
        $checkBlack = $redis->get($key_black);
        if ($checkBlack){
            return false;
        }
        if ($count >= Constant::CHECK_MESSAGE_COUNT){
            $redis->set($key_black,$uid,Constant::CHECK_BLACK_UID);
            return false;
        }
        $redis->set($key,$count,Constant::CHECK_BLACK_TIME); //2秒发送超过次数拉入黑名单无法发送
        return true;
    }

    //发送公共消息
    public function saveAllMessage($data)
    {
        if (!empty($data['uid']) && !empty($data['message'])){
            $redis = new Redis();
            $key   = 'checkMessageAll:'.$data['uid'];
            $redis->set($key,userTextEncode($data['message']),Constant::MESSAGE_ALL_TIME);
        }
    }

    //获取用昵称和头像
    public function getUserInfo($uid): array
    {
        if ($uid){
            try {
                $user =  Db::name(Constant::USER)
                    ->where(['uid' => $uid])
                    ->field('nick_name nick,avatar')
                    ->find();
                $avatar = empty($user['avatar']) ? Constant::AVATAR : $user['avatar'];
                return ['nick'=>$user['nick']??'匿名','avatar'=>$avatar];
            } catch (DataNotFoundException|ModelNotFoundException|DbException $e) {
                Log::write('获取用户信息失败：'.$e->getMessage());
                return ['nick'=>'','avatar'=>''];
            }
        }
       return ['nick'=>'','avatar'=>''];
    }

    //连接socket时离线消息推送
    public function offline_message($uid): array
    {
        try {
            $where['to_uid']     = $uid;  //对方是我未接收
            $where['status']  = 1;
            $where['is_read'] = 1;
            $message = Db::name(Constant::MESSAGE)
                ->where($where)
                ->select()
                ->toArray();
//            echo Db::getLastSql().PHP_EOL;
//            print_r($message);
//            echo PHP_EOL;
            $customer = Db::name(Constant::MESSAGE_CUSTOMER)
                ->where($where)
                ->select()
                ->toArray();
//            echo Db::getLastSql().PHP_EOL;
            return array_merge($message,$customer);
        }catch (Exception $e){
            Log::write('时离线消息推送失败：'.$e->getMessage());
        }
        return [];
    }

    // 表情处理函数
    public function processEmoji($content) {
        $emojiMap = [
            '[微笑]' => '😊',
            '[大哭]' => '😭',
            // 更多表情映射...
        ];
        return strtr($content, $emojiMap);
    }

    public function sendData($message,$message_id): array
    {
        try {
            $user = $this->getUserInfo($message['uid']);
            $send['type']   = $message['type']??'send_to_uid';
            $send['nick']   = $user['nick'];
            $send['uid']    = $message['uid'];
            $send['avatar'] = $user['avatar'];
            $send['send_time'] = date('Y-m-d H:i:s');
            $send['time']      = date('Y-m-d H:i:s');
            $send['message_id'] = $message_id??0;
            $send['message']    = empty($message['message']) ? '': userTextDecode($message['message']);
            $send['message_type']= empty($message['message_type']) ? '' : $message['message_type'];  //image 图片 text 普通消息
            return $send;
        }catch (Exception $e){
            Log::write('sendData保存失败：'.$e->getMessage());
        }
       return [];
    }
}
